package com.frejoys.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.frejoys.common.dto.admin.*;
import com.frejoys.common.dto.app.CodeDto;
import com.frejoys.common.dto.admin.StoreCodeIdDto;
import com.frejoys.common.dto.app.StoreCodeDto;
import com.frejoys.common.enums.EFileSize;
import com.frejoys.common.enums.EStoreCodType;
import com.frejoys.common.enums.EStoreCodeDepositoryStatus;
import com.frejoys.common.error.HttpStatus;
import com.frejoys.common.exception.GeneralException;
import com.frejoys.common.service.AliStorageService;
import com.frejoys.common.util.AppConfigUtil;
import com.frejoys.common.vo.admin.StoreCodeListVo;
import com.frejoys.common.vo.app.StoreIdAndTypeVo;
import com.frejoys.common.vo.app.StoreIdVo;
import com.frejoys.common.vo.store.StoreCodeVo;
import com.frejoys.dao.entity.Store;
import com.frejoys.dao.entity.StoreCode;
import com.frejoys.dao.entity.StoreCodeDepository;
import com.frejoys.dao.mapper.StoreCodeDepositoryMapper;
import com.frejoys.dao.mapper.StoreCodeMapper;
import com.frejoys.dao.mapper.StoreMapper;
import com.frejoys.service.StoreCodeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author wangzhicheng
 * @version 1.0
 * @date 2024/5/16 17:26
 */
@Service
@RequiredArgsConstructor
public class StoreCodeServiceImpl extends ServiceImpl<StoreCodeMapper, StoreCode> implements StoreCodeService {

    private final StoreCodeDepositoryMapper storeCodeDepositoryMapper;

    private final StoreMapper storeMapper;

    private final AliStorageService aliStorageService;
    @Override
    public boolean upPayCode(Integer storeId, Integer type, String payCode) {
        LambdaUpdateWrapper<StoreCode> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(StoreCode::getPayCode, payCode);
        wrapper.eq(StoreCode::getStoreId, storeId).eq(StoreCode::getType, type);
        return update(wrapper);
    }

    /**
     * 根据店铺id获取店铺支付码
     *
     * @param storeId
     */
    @Override
    public List<StoreCodeVo> listCode(Integer storeId) {
        LambdaQueryWrapper<StoreCode> wrapper = Wrappers.lambdaQuery();
        wrapper.select(StoreCode::getPayCode, StoreCode::getType).eq(StoreCode::getStoreId, storeId);
        List<StoreCode> storeCodes = baseMapper.selectList(wrapper);
        if (CollectionUtil.isEmpty(storeCodes)) {
            return Collections.emptyList();
        }
        return BeanUtil.copyToList(storeCodes, StoreCodeVo.class);
    }

    /**
     * 根据店铺id获取线上码
     */
    @Override
    public List<StoreCode> getOnlineCode() {
        LambdaQueryWrapper<StoreCode> wrapper = Wrappers.lambdaQuery();
        wrapper.select(StoreCode::getStoreId,StoreCode::getPayCode).eq(StoreCode::getType, EStoreCodType.online.getValue());
        return baseMapper.selectList(wrapper);
    }

    /**
     * 根据店铺id获取店铺线上支付码
     *
     * @param storeId
     */
    @Override
    public String getOnlineCode(Integer storeId) {
        LambdaQueryWrapper<StoreCode> wrapper = Wrappers.lambdaQuery();
        wrapper.select(StoreCode::getPayCode).eq(StoreCode::getStoreId, storeId).eq(StoreCode::getType, EStoreCodType.online.getValue());
        StoreCode storeCode = getOnly(wrapper);
        if(ObjectUtil.isNotEmpty(storeCode)){
            return storeCode.getPayCode();
        }
        return null;
    }

    /**
     * 新增店铺支付码
     *
     * @param storeId 店铺id
     * @param payCode 支付码
     * @param type    类型：1.线上码，2.线下码
     */
    @Override
    public boolean addStoreCode(Integer storeId, Long code, String payCode, Integer type) {
        return save(new StoreCode() {{
            setCode(code);
            setStoreId(storeId);
            setPayCode(payCode);
            setType(type);
        }});
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer deleteStoreCode(StoreCodeIdDto dto) {
        LambdaQueryWrapper<StoreCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StoreCode::getId,dto.getId()).eq(StoreCode::getType, EStoreCodType.offLine.getValue());

        StoreCode storeCode = getOnly(wrapper);

        if (storeCode == null){
            throw new GeneralException(HttpStatus.notStoreCodeError);
        }

        LambdaUpdateWrapper<StoreCodeDepository> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(StoreCodeDepository::getCode,storeCode.getCode())
                .set(StoreCodeDepository::getStatus,EStoreCodeDepositoryStatus.await.getValue());
        storeCodeDepositoryMapper.update(updateWrapper);

        return baseMapper.delete(wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addStoreCodeInfo(Integer storeId,Integer storeCodeDepositoryId, Integer type) {

        LambdaQueryWrapper<StoreCodeDepository> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(StoreCodeDepository::getStatus,StoreCodeDepository::getCode)
                .eq(StoreCodeDepository::getId,storeCodeDepositoryId);
        StoreCodeDepository storeCodeDepository = storeCodeDepositoryMapper.selectOne(wrapper);

        if (storeCodeDepository == null){
            throw new GeneralException(HttpStatus.notStoreCodeError);
        }
        if (storeCodeDepository.getStatus() == EStoreCodeDepositoryStatus.already.getValue()){
            throw new GeneralException(HttpStatus.alreadyStoreCodeError);
        }
        LambdaQueryWrapper<StoreCode> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(StoreCode::getId).eq(StoreCode::getStoreId,storeId).eq(StoreCode::getType,EStoreCodType.offLine.getValue());
        StoreCode storeCode = getOnly(queryWrapper);
        //如果店铺存在支付码 就换绑
        if (ObjectUtil.isNotEmpty(storeCode)){
            upStoreCode(storeId,storeCodeDepositoryId,EStoreCodType.offLine.getValue());
        }
        save(new StoreCode() {{
            setCode(storeCodeDepository.getCode());
            setStoreId(storeId);
//            setPayCode(payCode);
            setType(type);
        }});

        LambdaUpdateWrapper<StoreCodeDepository> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(StoreCodeDepository::getId,storeCodeDepositoryId)
                .set(StoreCodeDepository::getStatus,EStoreCodeDepositoryStatus.already.getValue());
        storeCodeDepositoryMapper.update(updateWrapper);
        return true;
    }


    @Override
    public Integer storeIdInfo(CodeDto dto) {
        LambdaQueryWrapper<StoreCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(StoreCode::getStoreId)
                .eq(StoreCode::getCode,dto.getCode());
        StoreCode storeCode = getOnly(wrapper);
        return storeCode.getStoreId();
    }


    @Override
    public StoreIdAndTypeVo storeIdAndType(StoreCodeDto dto) {
        LambdaQueryWrapper<StoreCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(StoreCode::getStoreId,StoreCode::getType).eq(StoreCode::getCode,dto.getCode());
        StoreCode storeCode = getOnly(wrapper);
        if (storeCode == null){
            throw new GeneralException(HttpStatus.codeError);
        }
        return BeanUtil.copyProperties(storeCode,StoreIdAndTypeVo.class);
    }

    @Override
    public Integer getStoreId(Long code) {

        LambdaQueryWrapper<StoreCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(StoreCode::getStoreId)
                .eq(StoreCode::getCode,code);
        StoreCode storeCode = getOnly(wrapper);

        if (storeCode == null){
            throw new GeneralException(HttpStatus.storeNotFound);
        }
        return storeCode.getStoreId();
    }

    @Override
    public Long getCode(Integer storeId, Integer type) {
        LambdaQueryWrapper<StoreCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(StoreCode::getCode).eq(StoreCode::getStoreId, storeId).eq(StoreCode::getType, type);
        StoreCode m = getOnly(wrapper);
        return m != null ? m.getCode() : null;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean upStoreCode(Integer storeId, Integer storeCodeDepositoryId, Integer type) {
        //查询支付码
        LambdaQueryWrapper<StoreCodeDepository> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(StoreCodeDepository::getStatus,StoreCodeDepository::getCode)
                .eq(StoreCodeDepository::getId,storeCodeDepositoryId);
        StoreCodeDepository storeCodeDepository = storeCodeDepositoryMapper.selectOne(wrapper);

        if (storeCodeDepository == null){
            throw new GeneralException(HttpStatus.notStoreCodeError);
        }
        if (storeCodeDepository.getStatus() == EStoreCodeDepositoryStatus.already.getValue()){
            throw new GeneralException(HttpStatus.alreadyStoreCodeError);
        }
        LambdaQueryWrapper<StoreCode> storeCodeWrapper = new LambdaQueryWrapper<>();
        storeCodeWrapper.select(StoreCode::getId,StoreCode::getCode).eq(StoreCode::getStoreId,storeId).eq(StoreCode::getType,type);
        StoreCode storeCode = getOnly(storeCodeWrapper);

        if (ObjectUtil.isNotEmpty(storeCode)){
            //修改店铺支付码
            LambdaUpdateWrapper<StoreCode> StoreCodeUpWrapper = new LambdaUpdateWrapper<>();
            StoreCodeUpWrapper.eq(StoreCode::getId,storeCode.getId())
                    .set(StoreCode::getCode,storeCodeDepository.getCode());
            baseMapper.update(StoreCodeUpWrapper);

            //修改原支付码状态
            LambdaUpdateWrapper<StoreCodeDepository> upWrapper = new LambdaUpdateWrapper<>();
            upWrapper.eq(StoreCodeDepository::getCode,storeCode.getCode())
                    .set(StoreCodeDepository::getStatus, EStoreCodeDepositoryStatus.await.getValue());
            storeCodeDepositoryMapper.update(upWrapper);
        }else {
            //没有支付码就添加
            save(new StoreCode() {{
                setCode(storeCodeDepository.getCode());
                setStoreId(storeId);
//            setPayCode(payCode);
                setType(type);
            }});
        }

        //修改新支付码状态
        LambdaUpdateWrapper<StoreCodeDepository> storeCodeDepositoryUpWrapper = new LambdaUpdateWrapper<>();
        storeCodeDepositoryUpWrapper.eq(StoreCodeDepository::getId,storeCodeDepositoryId)
                .set(StoreCodeDepository::getStatus, EStoreCodeDepositoryStatus.already.getValue());
        storeCodeDepositoryMapper.update(storeCodeDepositoryUpWrapper);
        return true;
    }



    @Override
    public IPage<StoreCodeListVo> storeAndCodeListInfo(StoreCodeListDto dto) {
        Page<StoreCode> page = new Page<>(dto.getPage(), dto.getSize());

        LambdaQueryWrapper<StoreCode> wrapper = new LambdaQueryWrapper<>();
        if (StrUtil.isNotEmpty(dto.getName())){
            List<Store> stores = storeMapper.getStoreNameById(dto.getName());
            if (stores.isEmpty()){
                return new Page<>();
            }
            List<Integer> ids = stores.stream().map(Store::getId).toList();
            wrapper.in(StoreCode::getStoreId,ids);
        }
        if (dto.getCode() != null){
            wrapper.eq(StoreCode::getCode,dto.getCode());
        }
        if (dto.getType() != null){
            wrapper.eq(StoreCode::getType,dto.getType());
        }
        IPage<StoreCode> selectPage = baseMapper.selectPage(page, wrapper);
        IPage<StoreCodeListVo> storeAndCodeVoIPage = selectPage.convert(storeCode -> BeanUtil.copyProperties(storeCode, StoreCodeListVo.class));
        List<StoreCodeListVo> list = storeAndCodeVoIPage.getRecords();

        if (!list.isEmpty()){

            List<Integer> storeIds = list.stream().map(StoreCodeListVo::getStoreId).toList();
            List<Store> stores = storeMapper.getStoreNameList(storeIds);
            Map<Integer, Store> storeMap = stores.stream().collect(Collectors.toMap(Store::getId, store -> store));

            list.forEach(vo ->{

                if (vo.getType() == EStoreCodType.online.getValue()){
                    vo.setPayCode(AppConfigUtil.getUrlPrefix(vo.getPayCode(),EFileSize.qrcode.getValue()));
                }else if (vo.getType() == EStoreCodType.offLine.getValue()){
                    vo.setPayCode(aliStorageService.createQrCodeLogoPayImage(String.valueOf(vo.getCode())));
                }
                if (storeMap.containsKey(vo.getStoreId())){
                    vo.setName(storeMap.get(vo.getId()).getName());
                    vo.setLogo(storeMap.get(vo.getId()).getLogo());
                    vo.setLogoUrl(AppConfigUtil.getUrlPrefix(storeMap.get(vo.getId()).getLogo(),EFileSize.logo.getValue()));
                }
            });
        }
        return storeAndCodeVoIPage;
    }

}
